﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using King.Engine.Core;
using King.Engine.Content;

namespace King.Engine.GameObjects
{
    /// <summary>
    /// An Animation traverses and draws the frames from a spritesheet
    /// </summary>
    public class Animation
    {

        #region Fields

        private Texture2D _texture;
        private bool _completed;
        private Rectangle _frameBounds;
        private SpriteEffects _spriteEffects;
        private int _frameIndex;
        private int _frameElapsedTime;
        
        #endregion


        #region Constants

        private readonly float _layerDepth;
        private readonly int _frameWidth;
        private readonly int _frameHeight;
        private readonly int _numFrames;
        private readonly int _numColumns;

        #endregion


        #region Properties

        /// <summary>
        /// Time per frame, in milliseconds
        /// </summary>
        public int FrameTime;

        /// <summary>
        /// Set whether or not this animation loops. Default is true
        /// </summary>
        public bool IsLoop;

        /// <summary>
        /// set whether or not this animation is a GUI element
        /// </summary>
        public bool IsGUI;

        public bool IsHorizontallyFlipped
        {
            set
            {
                if (value)
                {
                    _spriteEffects |= SpriteEffects.FlipHorizontally;
                }
                else
                {
                    _spriteEffects &= ~SpriteEffects.FlipHorizontally;
                }
            }
        }

        public bool IsVerticallyFlipped
        {
            set
            {
                if (value)
                {
                    _spriteEffects |= SpriteEffects.FlipVertically;
                }
                else
                {
                    _spriteEffects &= ~SpriteEffects.FlipVertically;
                }
            }
        }

        /// <summary>
        /// Callback for when this is non-looping and the animation completes
        /// </summary>
        public Action Complete
        {
            set;
            private get;
        }

        #endregion


        #region Constructor

        /// <summary>
        /// Construct an animation with square frames, all in a single row
        /// </summary>
        public Animation(string animationName, int frameSize, int numFrames, int frameTime, float layerDepth)
            :this(animationName, frameSize, frameSize, numFrames, frameTime, numFrames, layerDepth)
        {
            
        }

        /// <summary>
        /// Construct an animation with the given frame size, number of frames, and number of columns in the spritesheet
        /// </summary>
        public Animation(string animationName, int frameWidth, int frameHeight, int numFrames, int frameTime, int numColumns, float layerDepth)
        {
            _texture = ContentLoader.Get<Texture2D>(animationName);

            _frameHeight = frameHeight;
            _frameWidth = frameWidth;
            _numColumns = numColumns;
            _numFrames = numFrames;
            _layerDepth = layerDepth;

            IsLoop = true;
            Complete = (() => { });
            FrameTime = frameTime;
            _spriteEffects = SpriteEffects.None;
            _frameBounds = new Rectangle(0, 0, frameWidth, frameHeight);
        }

        #endregion


        #region Public Methods

        /// <summary>
        /// Restart the animation
        /// </summary>
        public void Restart()
        {
            _completed = false;
            _frameIndex = 0;
            _frameElapsedTime = 0;
        }

        public void Update(GameTime gameTime)
        {
            if (_completed) return;

            _frameElapsedTime += gameTime.ElapsedGameTime.Milliseconds;
            int deltaFrame = _frameElapsedTime / FrameTime;
            _frameElapsedTime %= FrameTime;

            if (deltaFrame > 0)
            {
                _frameIndex += deltaFrame;
                if (!IsLoop && _frameIndex >= _numFrames)
                {
                    Complete();
                }
                else
                {
                    _frameIndex %= _numFrames;
                    UpdateBounds();
                }
            }
        }

        public void Draw(Rectangle bounds)
        {
            Screen.Draw(IsGUI, _texture, bounds, _frameBounds, Color.White, 0.0f, Vector2.Zero, _spriteEffects, _layerDepth);
        }

        #endregion


        #region Private Methods

        private void UpdateBounds()
        {
            int col = _frameIndex % _numColumns;
            int row = _frameIndex / _numColumns;

            int frameLocX = col * _frameWidth;
            int frameLocY = row * _frameHeight;

            _frameBounds = new Rectangle(frameLocX, frameLocY, _frameWidth, _frameHeight);
        }

        #endregion

    }
}
